import math

import pyclipper
import plotly.graph_objects as go


def line_intersect_polygon(polygon, lines, l, fig):
    """
    检查多边形与线段（孔和槽）是否有交集

    参数:
        polygon: 多边形顶点列表，格式为 [[x1, y1], [x2, y2], ...]
        lines: 包含孔和槽的字典，格式为 {"holes": [(x, y), ...], "slots": [[(x1, y1), (x2, y2)], ...]}
        l: 偏移距离，用于创建行程范围
        fig: plotly 图形对象，用于可视化

    返回:
        bool: 如果有交集返回 True，否则返回 False
    """

    # 计算多边形的边界框
    min_x = min(p[0] for p in polygon)
    max_x = max(p[0] for p in polygon)
    min_y = min(p[1] for p in polygon)
    max_y = max(p[1] for p in polygon)
    max_rectangle = [
        [min_x, min_y],
        [max_x, min_y],
        [max_x, max_y],
        [min_x, max_y],
    ]

    # 使用 pyclipper 创建偏移多边形
    pc = pyclipper.PyclipperOffset()
    pc.AddPath(max_rectangle, pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON)
    scaled_poly = pc.Execute(l)
    if not scaled_poly:
        return True  # 如果无法创建偏移多边形，返回 True 表示有交集
    scaled_poly = scaled_poly[0]

    # 计算偏移多边形的边界框
    scaled_min_x = min(p[0] for p in scaled_poly)
    scaled_max_x = max(p[0] for p in scaled_poly)
    scaled_min_y = min(p[1] for p in scaled_poly)
    scaled_max_y = max(p[1] for p in scaled_poly)

    # 创建偏移矩形用于可视化
    scaled_rectangle_points = [
        [scaled_min_x, scaled_min_y],
        [scaled_max_x, scaled_min_y],
        [scaled_max_x, scaled_max_y],
        [scaled_min_x, scaled_max_y],
    ]

    # 添加偏移矩形到图形
    fig.add_trace(
        go.Scatter(
            x=[item[0] for item in scaled_rectangle_points],
            y=[item[1] for item in scaled_rectangle_points],
            fill="toself",
            mode="lines",
            line=dict(color="purple"),
            name="行程范围图形",
        )
    )

    # 创建偏移矩形的边界信息
    scaled_rectangle = {
        "min_x": scaled_min_x,
        "max_x": scaled_max_x,
        "min_y": scaled_min_y,
        "max_y": scaled_max_y,
    }

    # 检查是否有任何线段在行程范围内
    has_intersection = False

    # 判断路径是否在行程范围外
    for key, value in lines.items():
        if key == "holes":
            for hole in value:
                # 判断孔是否在行程范围内
                is_in_range = is_point_in_range(hole, scaled_rectangle)
                print("孔： ", hole, " 行程", is_in_range)

                # 如果孔在行程范围内，标记有交集
                if is_in_range:
                    has_intersection = True

                # 绘制孔
                fig.add_trace(
                    go.Scatter(
                        x=[hole[0]],
                        y=[hole[1]],
                        mode="markers",
                        marker=dict(color="red", size=8),
                        name=f"hole + {is_in_range}",
                    )
                )

        if key == "slots":
            for slot in value:
                # 判断槽是否在行程范围内
                is_in_range = is_line_in_range(slot, scaled_rectangle)

                print("槽：", slot, " 行程", is_in_range)

                # 如果槽在行程范围内，标记有交集
                if is_in_range:
                    has_intersection = True

                # 绘制槽
                fig.add_trace(
                    go.Scatter(
                        x=[slot[0][0], slot[1][0]],
                        y=[slot[0][1], slot[1][1]],
                        mode="lines+markers",
                        line=dict(color="orange", width=2),
                        name=f"slot + {is_in_range}",
                    )
                )

    return has_intersection


def show_ui(polygon, lines, fig):
    """
    在图形上显示多边形和线段

    参数:
        polygon: 多边形顶点列表
        lines: 包含孔和槽的字典
        fig: plotly 图形对象
    """
    # 处理多边形顶点，闭合多边形
    poly_x = [p[0] for p in polygon] + [polygon[0][0]]
    poly_y = [p[1] for p in polygon] + [polygon[0][1]]

    # 绘制多边形
    fig.add_trace(
        go.Scatter(
            x=poly_x,
            y=poly_y,
            fill="toself",
            mode="lines",
            line=dict(color="blue"),
            name="原始图形",
        )
    )

    fig.update_layout(
        title="多边形与线段可视化",
        xaxis_title="X",
        yaxis_title="Y",
        showlegend=True,
        width=1800,
        height=1200,
    )


def is_point_in_range(point, scale_shape):
    """
    判断一个点是否在行程范围内

    参数:
        point: 点坐标 [x, y]
        scale_shape: 行程范围的边界信息

    返回:
        bool: 如果点在行程范围内返回 True，否则返回 False
    """
    return (scale_shape["min_x"] < point[0] < scale_shape["max_x"]) and (
        scale_shape["min_y"] < point[1] < scale_shape["max_y"]
    )


def is_line_in_range(line, scale_shape):
    """
    判断一条线段是否在行程范围内

    参数:
        line: 线段的两个端点 [[x1, y1], [x2, y2]]
        scale_shape: 行程范围的边界信息

    返回:
        bool: 如果线段在行程范围内返回 True，否则返回 False
    """
    # 计算线段长度
    length = math.sqrt((line[0][0] - line[1][0]) ** 2 + (line[0][1] - line[1][1]) ** 2)

    # 检查两个端点是否都在行程范围内
    point1_in_range = is_point_in_range(line[0], scale_shape)
    point2_in_range = is_point_in_range(line[1], scale_shape)

    # 如果两个端点都在行程范围内，则线段在行程范围内
    if point1_in_range and point2_in_range:
        return True

    # 如果只有一个端点在行程范围内，则线段与行程范围有交集
    if point1_in_range or point2_in_range:
        return True

    # 如果两个端点都不在行程范围内，检查线段是否穿过行程范围

    # 水平线段
    if abs(line[0][1] - line[1][1]) < 0.01:
        # 检查水平线段是否穿过行程范围
        y = line[0][1]
        min_x = min(line[0][0], line[1][0])
        max_x = max(line[0][0], line[1][0])

        # 如果线段的 y 坐标在行程范围内，且线段足够长以穿过行程范围
        if (
            scale_shape["min_y"] < y < scale_shape["max_y"]
            and length >= scale_shape["max_x"] -  scale_shape["min_x"]
        ):
            return True

    # 垂直线段
    elif abs(line[0][0] - line[1][0]) < 0.01:
        # 检查垂直线段是否穿过行程范围
        x = line[0][0]
        min_y = min(line[0][1], line[1][1])
        max_y = max(line[0][1], line[1][1])

        # 如果线段的 x 坐标在行程范围内，且线段足够长以穿过行程范围
        if (
            scale_shape["min_x"] < x < scale_shape["max_x"]
            and length >= scale_shape["max_y"] - scale_shape["min_y"]
        ):
            return True

    # 斜线段 - 使用线段与矩形边界的交点检测
    else:
        # 检查线段是否与行程范围的四条边有交点
        if line_intersects_rectangle(line, scale_shape):
            return True

    return False


def line_intersects_rectangle(line, rect):
    """
    检查线段是否与矩形相交

    参数:
        line: 线段的两个端点 [[x1, y1], [x2, y2]]
        rect: 矩形的边界信息

    返回:
        bool: 如果线段与矩形相交返回 True，否则返回 False
    """
    # 矩形的四条边
    rect_edges = [
        [[rect["min_x"], rect["min_y"]], [rect["max_x"], rect["min_y"]]],  # 下边
        [[rect["max_x"], rect["min_y"]], [rect["max_x"], rect["max_y"]]],  # 右边
        [[rect["max_x"], rect["max_y"]], [rect["min_x"], rect["max_y"]]],  # 上边
        [[rect["min_x"], rect["max_y"]], [rect["min_x"], rect["min_y"]]],  # 左边
    ]

    # 检查线段是否与矩形的任意一条边相交
    for edge in rect_edges:
        if line_segments_intersect(line, edge):
            return True

    return False


def line_segments_intersect(line1, line2):
    """
    检查两条线段是否相交

    参数:
        line1: 第一条线段的两个端点 [[x1, y1], [x2, y2]]
        line2: 第二条线段的两个端点 [[x1, y1], [x2, y2]]

    返回:
        bool: 如果两条线段相交返回 True，否则返回 False
    """
    # 提取线段的端点
    x1, y1 = line1[0]
    x2, y2 = line1[1]
    x3, y3 = line2[0]
    x4, y4 = line2[1]

    # 计算方向
    d1 = direction(x3, y3, x4, y4, x1, y1)
    d2 = direction(x3, y3, x4, y4, x2, y2)
    d3 = direction(x1, y1, x2, y2, x3, y3)
    d4 = direction(x1, y1, x2, y2, x4, y4)

    # 如果两条线段相交，则它们的方向会不同
    if ((d1 > 0 and d2 < 0) or (d1 < 0 and d2 > 0)) and (
        (d3 > 0 and d4 < 0) or (d3 < 0 and d4 > 0)
    ):
        return True

    # 检查共线的情况
    if d1 == 0 and on_segment(x3, y3, x4, y4, x1, y1):
        return True
    if d2 == 0 and on_segment(x3, y3, x4, y4, x2, y2):
        return True
    if d3 == 0 and on_segment(x1, y1, x2, y2, x3, y3):
        return True
    if d4 == 0 and on_segment(x1, y1, x2, y2, x4, y4):
        return True

    return False


def direction(x1, y1, x2, y2, x3, y3):
    """
    计算三点的方向

    参数:
        (x1, y1), (x2, y2), (x3, y3): 三个点的坐标

    返回:
        int: 正值表示顺时针，负值表示逆时针，0 表示共线
    """
    return (x3 - x1) * (y2 - y1) - (x2 - x1) * (y3 - y1)


def on_segment(x1, y1, x2, y2, x3, y3):
    """
    检查点 (x3, y3) 是否在线段 (x1, y1)-(x2, y2) 上

    参数:
        (x1, y1), (x2, y2): 线段的两个端点
        (x3, y3): 要检查的点

    返回:
        bool: 如果点在线段上返回 True，否则返回 False
    """
    return min(x1, x2) <= x3 <= max(x1, x2) and min(y1, y2) <= y3 <= max(y1, y2)


if __name__ == "__main__":
    # # 基础图形
    polygon = [
        [0, 0],
        [327, 0],
        [327, 1073],
        [0, 1073],
    ]
    # 简单的测试数据
    lines = {
        "holes": [
            # (22, 22),  # 在行程范围内的孔
            # (10, 10),  # 在行程范围内的孔
            # (5, 5),  # 在行程范围边缘的孔
            # (90, 90),  # 在行程范围内的孔
            # (110, 110)  # 在行程范围外的孔
        ],
        "slots": [

            [(20, 80), (80, 80)],
            [(20, 70), (80, 70)],
            [(20, 20), (80, 20)],
            [(20, 30), (80, 30)],
            [(80, 20), (80, 80)],
            [(70., 20), (70, 80)],

            # [(40, 40), (40, 0)],  # 穿过正方形的垂直线段
            # [(15, 5), (20, 5)],  # 水平线段
            # [(10, 90), (70, 90)],  # 水平线段
            # [(10, 60), (95, 60)],  # 水平线段
            # [(50, 10), (60, 90)],  # 垂直线段
            # [(0, 0), (100, 100)],  # 对角线
            # [(20, 80), (80, 80)],
            #
            # [(-10, 50), (110, 50)],  # 穿过矩形的水平线段
            # [(50, -10), (50, 110)],  # 穿过矩形的垂直线段
            # [(120, 120), (154, 155)]  # 完全在矩形外的线段
        ]
    }
    test_data = [[{'x': 114.494, 'y': 22.501}, {'x': 114.38, 'y': 20.684}, {'x': 114.039, 'y': 18.896}, {'x': 113.476, 'y': 17.165}, {'x': 112.701, 'y': 15.518}, {'x': 111.726, 'y': 13.982}, {'x': 110.566, 'y': 12.579}, {'x': 109.239, 'y': 11.333}, {'x': 107.766, 'y': 10.263}, {'x': 106.171, 'y': 9.386}, {'x': 104.479, 'y': 8.716}, {'x': 102.716, 'y': 8.264}, {'x': 100.91, 'y': 8.036}, {'x': 99.09, 'y': 8.036}, {'x': 97.284, 'y': 8.264}, {'x': 95.521, 'y': 8.716}, {'x': 93.829, 'y': 9.386}, {'x': 92.234, 'y': 10.263}, {'x': 90.761, 'y': 11.333}, {'x': 89.434, 'y': 12.579}, {'x': 88.274, 'y': 13.982}, {'x': 87.299, 'y': 15.518}, {'x': 86.524, 'y': 17.165}, {'x': 85.961, 'y': 18.896}, {'x': 85.62, 'y': 20.684}, {'x': 85.506, 'y': 22.501}, {'x': 85.62, 'y': 24.318}, {'x': 85.961, 'y': 26.106}, {'x': 86.524, 'y': 27.837}, {'x': 87.299, 'y': 29.484}, {'x': 88.274, 'y': 31.02}, {'x': 89.434, 'y': 32.423}, {'x': 90.761, 'y': 33.669}, {'x': 92.234, 'y': 34.739}, {'x': 93.829, 'y': 35.616}, {'x': 95.521, 'y': 36.286}, {'x': 97.284, 'y': 36.738}, {'x': 99.09, 'y': 36.966}, {'x': 100.91, 'y': 36.966}, {'x': 102.716, 'y': 36.738}, {'x': 104.479, 'y': 36.286}, {'x': 106.171, 'y': 35.616}, {'x': 107.766, 'y': 34.739}, {'x': 109.239, 'y': 33.669}, {'x': 110.566, 'y': 32.423}, {'x': 111.726, 'y': 31.02}, {'x': 112.701, 'y': 29.484}, {'x': 113.476, 'y': 27.837}, {'x': 114.039, 'y': 26.106}, {'x': 114.38, 'y': 24.318}, {'x': 114.494, 'y': 22.501}, {'x': 111.488, 'y': 22.501}, {'x': 111.398, 'y': 21.06}, {'x': 111.128, 'y': 19.644}, {'x': 110.681, 'y': 18.272}, {'x': 110.067, 'y': 16.966}, {'x': 109.294, 'y': 15.749}, {'x': 108.375, 'y': 14.636}, {'x': 107.323, 'y': 13.649}, {'x': 106.155, 'y': 12.801}, {'x': 104.891, 'y': 12.106}, {'x': 103.551, 'y': 11.575}, {'x': 102.153, 'y': 11.217}, {'x': 100.721, 'y': 11.036}, {'x': 99.279, 'y': 11.036}, {'x': 97.847, 'y': 11.217}, {'x': 96.449, 'y': 11.575}, {'x': 95.109, 'y': 12.106}, {'x': 93.845, 'y': 12.801}, {'x': 92.677, 'y': 13.649}, {'x': 91.625, 'y': 14.636}, {'x': 90.706, 'y': 15.749}, {'x': 89.933, 'y': 16.966}, {'x': 89.319, 'y': 18.272}, {'x': 88.872, 'y': 19.644}, {'x': 88.602, 'y': 21.06}, {'x': 88.512, 'y': 22.501}, {'x': 88.602, 'y': 23.942}, {'x': 88.872, 'y': 25.358}, {'x': 89.319, 'y': 26.73}, {'x': 89.933, 'y': 28.036}, {'x': 90.706, 'y': 29.253}, {'x': 91.625, 'y': 30.366}, {'x': 92.677, 'y': 31.353}, {'x': 93.845, 'y': 32.201}, {'x': 95.109, 'y': 32.896}, {'x': 96.449, 'y': 33.427}, {'x': 97.847, 'y': 33.785}, {'x': 99.279, 'y': 33.966}, {'x': 100.721, 'y': 33.966}, {'x': 102.153, 'y': 33.785}, {'x': 103.551, 'y': 33.427}, {'x': 104.891, 'y': 32.896}, {'x': 106.155, 'y': 32.201}, {'x': 107.323, 'y': 31.353}, {'x': 108.375, 'y': 30.366}, {'x': 109.294, 'y': 29.253}, {'x': 110.067, 'y': 28.036}, {'x': 110.681, 'y': 26.73}, {'x': 111.128, 'y': 25.358}, {'x': 111.398, 'y': 23.942}, {'x': 111.488, 'y': 22.501}, {'x': 108.482, 'y': 22.501}, {'x': 108.416, 'y': 21.436}, {'x': 108.217, 'y': 20.393}, {'x': 107.886, 'y': 19.379}, {'x': 107.433, 'y': 18.414}, {'x': 106.862, 'y': 17.515}, {'x': 106.183, 'y': 16.693}, {'x': 105.408, 'y': 15.966}, {'x': 104.545, 'y': 15.339}, {'x': 103.611, 'y': 14.826}, {'x': 102.623, 'y': 14.434}, {'x': 101.591, 'y': 14.17}, {'x': 100.532, 'y': 14.036}, {'x': 99.468, 'y': 14.036}, {'x': 98.409, 'y': 14.17}, {'x': 97.377, 'y': 14.434}, {'x': 96.389, 'y': 14.826}, {'x': 95.455, 'y': 15.339}, {'x': 94.592, 'y': 15.966}, {'x': 93.817, 'y': 16.693}, {'x': 93.138, 'y': 17.515}, {'x': 92.567, 'y': 18.414}, {'x': 92.114, 'y': 19.379}, {'x': 91.783, 'y': 20.393}, {'x': 91.584, 'y': 21.436}, {'x': 91.518, 'y': 22.501}, {'x': 91.584, 'y': 23.566}, {'x': 91.783, 'y': 24.609}, {'x': 92.114, 'y': 25.623}, {'x': 92.567, 'y': 26.588}, {'x': 93.138, 'y': 27.487}, {'x': 93.817, 'y': 28.309}, {'x': 94.592, 'y': 29.036}, {'x': 95.455, 'y': 29.663}, {'x': 96.389, 'y': 30.176}, {'x': 97.377, 'y': 30.568}, {'x': 98.409, 'y': 30.832}, {'x': 99.468, 'y': 30.966}, {'x': 100.532, 'y': 30.966}, {'x': 101.591, 'y': 30.832}, {'x': 102.623, 'y': 30.568}, {'x': 103.611, 'y': 30.176}, {'x': 104.545, 'y': 29.663}, {'x': 105.408, 'y': 29.036}, {'x': 106.183, 'y': 28.309}, {'x': 106.862, 'y': 27.487}, {'x': 107.433, 'y': 26.588}, {'x': 107.886, 'y': 25.623}, {'x': 108.217, 'y': 24.609}, {'x': 108.416, 'y': 23.566}, {'x': 108.482, 'y': 22.501}, {'x': 105.476, 'y': 22.501}, {'x': 105.433, 'y': 21.811}, {'x': 105.306, 'y': 21.143}, {'x': 105.092, 'y': 20.486}, {'x': 104.798, 'y': 19.861}, {'x': 104.43, 'y': 19.281}, {'x': 103.991, 'y': 18.751}, {'x': 103.493, 'y': 18.283}, {'x': 102.935, 'y': 17.877}, {'x': 102.331, 'y': 17.546}, {'x': 101.694, 'y': 17.293}, {'x': 101.029, 'y': 17.123}, {'x': 100.343, 'y': 17.036}, {'x': 99.657, 'y': 17.036}, {'x': 98.971, 'y': 17.123}, {'x': 98.306, 'y': 17.293}, {'x': 97.669, 'y': 17.546}, {'x': 97.065, 'y': 17.877}, {'x': 96.507, 'y': 18.283}, {'x': 96.009, 'y': 18.751}, {'x': 95.57, 'y': 19.281}, {'x': 95.202, 'y': 19.861}, {'x': 94.908, 'y': 20.486}, {'x': 94.694, 'y': 21.143}, {'x': 94.567, 'y': 21.811}, {'x': 94.524, 'y': 22.501}, {'x': 94.567, 'y': 23.191}, {'x': 94.694, 'y': 23.859}, {'x': 94.908, 'y': 24.516}, {'x': 95.202, 'y': 25.141}, {'x': 95.57, 'y': 25.721}, {'x': 96.009, 'y': 26.251}, {'x': 96.507, 'y': 26.719}, {'x': 97.065, 'y': 27.125}, {'x': 97.669, 'y': 27.456}, {'x': 98.306, 'y': 27.709}, {'x': 98.971, 'y': 27.879}, {'x': 99.657, 'y': 27.966}, {'x': 100.343, 'y': 27.966}, {'x': 101.029, 'y': 27.879}, {'x': 101.694, 'y': 27.709}, {'x': 102.331, 'y': 27.456}, {'x': 102.935, 'y': 27.125}, {'x': 103.493, 'y': 26.719}, {'x': 103.991, 'y': 26.251}, {'x': 104.43, 'y': 25.721}, {'x': 104.798, 'y': 25.141}, {'x': 105.092, 'y': 24.516}, {'x': 105.306, 'y': 23.859}, {'x': 105.433, 'y': 23.191}, {'x': 105.476, 'y': 22.501}, {'x': 102.47, 'y': 22.501}, {'x': 102.451, 'y': 22.186}, {'x': 102.394, 'y': 21.891}, {'x': 102.298, 'y': 21.593}, {'x': 102.164, 'y': 21.309}, {'x': 101.999, 'y': 21.049}, {'x': 101.8, 'y': 20.809}, {'x': 101.576, 'y': 20.598}, {'x': 101.325, 'y': 20.416}, {'x': 101.052, 'y': 20.266}, {'x': 100.765, 'y': 20.152}, {'x': 100.467, 'y': 20.076}, {'x': 100.154, 'y': 20.036}, {'x': 99.846, 'y': 20.036}, {'x': 99.533, 'y': 20.076}, {'x': 99.235, 'y': 20.152}, {'x': 98.948, 'y': 20.266}, {'x': 98.675, 'y': 20.416}, {'x': 98.424, 'y': 20.598}, {'x': 98.2, 'y': 20.809}, {'x': 98.001, 'y': 21.049}, {'x': 97.836, 'y': 21.309}, {'x': 97.702, 'y': 21.593}, {'x': 97.606, 'y': 21.891}, {'x': 97.549, 'y': 22.186}, {'x': 97.53, 'y': 22.501}, {'x': 97.549, 'y': 22.816}, {'x': 97.606, 'y': 23.111}, {'x': 97.702, 'y': 23.409}, {'x': 97.836, 'y': 23.693}, {'x': 98.001, 'y': 23.953}, {'x': 98.2, 'y': 24.193}, {'x': 98.424, 'y': 24.404}, {'x': 98.675, 'y': 24.586}, {'x': 98.948, 'y': 24.736}, {'x': 99.235, 'y': 24.85}, {'x': 99.533, 'y': 24.926}, {'x': 99.846, 'y': 24.966}, {'x': 100.154, 'y': 24.966}, {'x': 100.467, 'y': 24.926}, {'x': 100.765, 'y': 24.85}, {'x': 101.052, 'y': 24.736}, {'x': 101.325, 'y': 24.586}, {'x': 101.576, 'y': 24.404}, {'x': 101.8, 'y': 24.193}, {'x': 101.999, 'y': 23.953}, {'x': 102.164, 'y': 23.693}, {'x': 102.298, 'y': 23.409}, {'x': 102.394, 'y': 23.111}, {'x': 102.451, 'y': 22.816}, {'x': 102.47, 'y': 22.501}]]
    
    
    fig = go.Figure()
    l = -20
    show_ui(polygon, lines, fig)

    res = line_intersect_polygon(polygon, lines, l, fig)
    print("图形是否有交集：", res)
    fig.show()
    
    

